import csv
import os
import random
import shutil
import string
import tempfile
from logging import getLogger
from typing import Annotated

from fastapi import BackgroundTasks, Depends, HTTPException
from fastapi.responses import FileResponse
from sqlalchemy import select, update

from forward.api.admin.orm.lucky.activity import ActivityORM
from forward.api.admin.orm.lucky.code import CodeORM
from forward.api.admin.orm.lucky.code_batch import CodeBatchORM
from forward.api.admin.schemas.lucky.code_batch import (
    CodeBatchAddInSchema,
    CodeBatchDeleteInSchema,
    CodeBatchDetailInSchema,
    CodeBatchDetailOutSchema,
    CodeBatchEditInSchema,
    CodeBatchExportInSchema,
    CodeBatchGenerateInSchema,
    CodeBatchListInSchema,
    CodeBatchStatusInSchema,
)
from forward.common.enums import CodeBatchStatusEnum
from forward.config import get_settings
from forward.dependencies.database import db
from forward.exceptions.base import AppException
from forward.extend.pydantic.result_api import ResultAPI
from forward.http_base import HttpResp
from forward.model.schemas.lucky.code import CodeOutSchema
from forward.model.schemas.lucky.code_batch import (
    CodeBatchCreateSchema,
    CodeBatchUpdateSchema,
)
from forward.model.tables.activity import CodeBatchModel, CodeModel
from forward.utils.urls import UrlUtil

logger = getLogger("code_batch")


class CodeBatchService:
    def __init__(
        self,
        code_batch_orm: Annotated[CodeBatchORM, Depends()],
        code_orm: Annotated[CodeORM, Depends()],
        activity_orm: Annotated[ActivityORM, Depends()],
    ):
        self.code_batch_orm = code_batch_orm
        self.code_orm = code_orm
        self.activity_orm = activity_orm

    async def lists(self, list_in: CodeBatchListInSchema):

        pagination = await self.code_batch_orm.lists(
            page_num=list_in.page_num, page_size=list_in.page_size
        )
        lists = []
        # 完善返回字段
        for item in pagination.lists:
            data = item.model_dump()
            data["code_encoding"] = f"{item.prefix}.{item.begin_num}-{item.end_num}"
            data["status_text"] = CodeBatchStatusEnum.get_str(item.status)
            data["bind_activity_count"] = await self.code_orm.get_bind_activity_count(
                code_batch_id=item.id
            )
            data["unused_num"] = item.unused_num if item.unused_num else 0
            code_batch = CodeBatchDetailOutSchema(**data)
            lists.append(code_batch)
        return ResultAPI(data={"lists": lists, "count": pagination.total})

    async def add(self, add_in: CodeBatchAddInSchema):
        if add_in.prefix.count(".") > 0:
            add_in.prefix = add_in.prefix.replace(".", "")
        # 检测 是否重复编码
        code_batchs = await self.code_batch_orm.get_all_by_filters(prefix=add_in.prefix)
        for code_batch in code_batchs:
            if code_batch.begin_num <= add_in.begin_num <= code_batch.end_num:
                raise AppException(exc=HttpResp.FAILED, msg="添加失败，编码重复")

        # 添加
        code_batch = CodeBatchCreateSchema(**add_in.model_dump())
        await self.code_batch_orm.create(code_batch.model_dump())
        return ResultAPI()

    async def edit(self, edit_in: CodeBatchEditInSchema):

        update_in = CodeBatchUpdateSchema(**edit_in.model_dump())
        await self.code_batch_orm.update(id=edit_in.id, data=update_in.model_dump())
        return ResultAPI()

    async def detail(self, detail_in: CodeBatchDetailInSchema):
        code_batch = await self.code_batch_orm.get_one_by_id(id=detail_in.id)
        return ResultAPI(data=code_batch)

    async def delete(self, delete_in: CodeBatchDeleteInSchema):

        code_batch = await self.code_batch_orm.get_one_by_id(delete_in.id)
        if not code_batch:
            raise AppException(HttpResp.SYSTEM_ERROR, msg="码批次不存在")
        # 检测码号是否绑定有活动

        bind_activity_count = await self.code_orm.get_bind_activity_count(delete_in.id)
        if bind_activity_count > 0:
            raise AppException(HttpResp.FAILED, msg="码号已存在绑定活动，请先解绑")
        # 删除码批次
        await self.code_batch_orm.delete_by_id(delete_in.id)
        # 删除码号批次关联的码号
        await self.code_orm.delete_by_code_batch_id(delete_in.id)
        return ResultAPI()

    async def status(self, status_in: CodeBatchStatusInSchema):
        statement = (
            update(CodeBatchModel)
            .where(CodeBatchModel.id == status_in.id)
            .values(status_in.model_dump())
        )
        await db.execute(statement)
        return ResultAPI()

    async def generate(
        self, generate_in: CodeBatchGenerateInSchema, background_tasks: BackgroundTasks
    ):
        """
        生成码号
        1. 获取码批次信息
        #判断码批次状态
        2. 获取码批次下的码号信息
        3. 批量生成码号-结束并更新码批次状态 1:生成完成
        """

        await self.check_generate_code(generate_in.id)

        # 根据规则生成码号并入库
        background_tasks.add_task(self._generate_code, generate_in)

        return ResultAPI(msg="生成中", show=True)

    async def check_generate_code(self, code_batch_id: int):
        logger.info("检查码批次生成码")
        code_batch = await self.code_batch_orm.get_one_by_id(code_batch_id)
        if not code_batch:
            raise AppException(exc=HttpResp.FAILED, msg="批次不存在")

        # 判断是否有正在生成中的批次
        if code_batch.status == CodeBatchStatusEnum.PUTTING.value:
            raise AppException(exc=HttpResp.FAILED, msg="正在生成中，请稍后再试")
        # 判断是否重复入库
        if code_batch.status == CodeBatchStatusEnum.PUT_END.value:
            raise AppException(exc=HttpResp.FAILED, msg="已入库完成")
        #  更新批次状态 2:正在生成中
        await self.code_batch_orm.update(
            code_batch.id, {"status": CodeBatchStatusEnum.PUTTING.value}
        )
        return True

    async def _generate_code(self, generate_in: CodeBatchGenerateInSchema):
        """
        params:
            prefix: 码前缀
            begin_num: 开始码
            end_num: 结束码
            num: 生成数量
        """
        # 获取码批次信息
        code_batch = await self.code_batch_orm.get_one_by_id(generate_in.id)
        if not code_batch:
            raise AppException(exc=HttpResp.FAILED, detail="批次不存在")

        logger.info(f"开始生成码: {code_batch.prefix}")
        prefix = code_batch.prefix
        begin_num = code_batch.begin_num
        end_num = code_batch.end_num

        # 检测prefix 是否有多余点号
        # if prefix.count(".") > 0:
        #     prefix = prefix.replace(".", "")

        characters = string.ascii_lowercase + string.digits  # 数字和小写字母
        batch_size = 1000  # 每批生成的数量
        total_codes = []
        max_retries = 10
        for num in range(begin_num, end_num + 1):
            code_id = num
            code = None
            retries = 0

            while retries < max_retries:
                code = "".join(random.choice(characters) for _ in range(6))
                statement = select(CodeModel).where(
                    CodeModel.code == code, CodeModel.prefix == prefix
                )
                exists = await db.fetch_one(statement)
                if not exists:
                    break
                retries += 1

            if retries == max_retries:
                # 重置所有信息（删除已经生成码号，修改码号批次状态为未开始）
                # 带用户重新生成
                await self.reset_code_batch(generate_in.id)
                logger.error("无法生成唯一的码")
                raise AppException(HttpResp.SYSTEM_ERROR, msg="无法生成唯一的码")
            total_codes.append(
                {
                    "prefix": prefix,
                    "code_id": code_id,
                    "code": code,
                    "code_batch_id": generate_in.id,
                }
            )
            if len(total_codes) >= batch_size:
                logger.info(
                    f"正在生成码，当前进度：{len(total_codes)}/{code_batch.end_num-code_batch.begin_num+1}"
                )
                logger.info(f"{total_codes[0]}")
                await self.code_orm.bulk_create(total_codes)
                total_codes = []
                logger.info(f"完成一批")
        if total_codes:
            await self.code_orm.bulk_create(total_codes)
            pass
        # 更新状态
        await self.code_batch_orm.update(
            generate_in.id, {"status": CodeBatchStatusEnum.PUT_END.value}
        )
        return True

    async def reset_code_batch(self, code_batch_id: int):
        await self.code_orm.delete_by_code_batch_id(code_batch_id)
        return True

    async def export(self, export_in: CodeBatchExportInSchema):
        """
        导出文件 FileResponse
        """

        # 查询
        codes = await self.code_orm.get_all_by_filters(code_batch_id=export_in.id)
        # 导出
        root_path = os.path.dirname(os.path.abspath(__file__))

        base_url = "http://localhost:8000/"

        # 创建临时文件
        with tempfile.NamedTemporaryFile(
            delete=False, mode="w", newline="", encoding="utf-8", suffix=".csv"
        ) as temp_file:
            writer = csv.writer(temp_file)
            writer.writerow(["ID", "code"])  # 写入表头
            for code in codes:
                writer.writerow([f"{code.prefix}.{code.code_id}"])  # 写入数据行
            temp_file_path = temp_file.name  # 直接使用 temp_file.name

        # 假设我们将文件移动到 static/files 目录下
        static_files_dir = os.path.join(os.getcwd(), "static", "files")
        get_settings().static_directory
        os.makedirs(static_files_dir, exist_ok=True)  # 确保目录存在
        file_name = os.path.basename(temp_file_path)
        target_file_path = os.path.join(static_files_dir, file_name)
        # 复制文件
        shutil.copy(temp_file_path, target_file_path)
        # 删除原始临时文件
        os.remove(temp_file_path)

        relative_path = os.path.join("static", "files", file_name)
        down_load_url = base_url + relative_path
        with open(target_file_path, "rb") as f:
            content = f.read()

        print("文件路径", target_file_path)
        return FileResponse(
            target_file_path,
            filename=f"{file_name}",
            media_type="application/octet-stream;charset=utf-8",
        )

    async def get_file_download_url(self, export_in: CodeBatchExportInSchema):
        # 查询
        code_batch = await self.code_batch_orm.get_one_by_id(export_in.id)
        codes = await self.code_orm.get_all_by_filters(code_batch_id=export_in.id)
        if not code_batch:
            raise HTTPException(status_code=404, detail="码批次不存在")
        if code_batch.file_path:
            file_name = code_batch.file_path.split("/")[-1]
            return ResultAPI(
                data={
                    "fileUrl": await UrlUtil.to_absolute_url(code_batch.file_path),
                    "fileName": file_name,
                },
            )
        else:
            # 创建临时文件
            with tempfile.NamedTemporaryFile(
                delete=False, mode="w", newline="", encoding="utf-8", suffix=".csv"
            ) as temp_file:
                writer = csv.writer(temp_file)
                writer.writerow(["ID", "code"])  # 写入表头
                for code in codes:
                    writer.writerow(
                        [f"{code.prefix}.{code.code_id}", code.code]
                    )  # 写入数据行
                temp_file_path = temp_file.name  # 直接使用 temp_file.name
                file_name = os.path.basename(temp_file_path)
            static_files_dir = os.path.join(get_settings().static_directory, "files")
            static_files_uri = os.path.join("static", "files", file_name)
            target_file_path = os.path.join(static_files_dir, file_name)
            # 复制文件
            shutil.copy(temp_file_path, target_file_path)
            # 删除原始临时文件
            os.remove(temp_file_path)
            await self.code_batch_orm.update(
                export_in.id, {"file_path": static_files_uri.replace(os.sep, "/")}
            )

            return ResultAPI(
                data={
                    "fileUrl": await UrlUtil.to_absolute_url(
                        static_files_uri.replace(os.sep, "/")
                    ),
                    "fileName": file_name,
                }
            )
